Skip to content

Add Roslyn-format EnC CustomDebugInformation codec and portable PDB method CDI emission#20018

Draft
NatElkins wants to merge 2 commits into
dotnet:mainfrom
NatElkins:pdb-method-cdi-emission
Draft

Add Roslyn-format EnC CustomDebugInformation codec and portable PDB method CDI emission#20018
NatElkins wants to merge 2 commits into
dotnet:mainfrom
NatElkins:pdb-method-cdi-emission

Conversation

@NatElkins

Copy link
Copy Markdown
Contributor

Summary

Adds an internal AbstractIL module (EncMethodDebugInformation) implementing, byte for byte, the three Portable PDB CustomDebugInformation blob formats Roslyn persists per method for Edit and Continue:

  • EnC Local Slot Map (755F52A8-91C5-45BE-B4B8-209571E552BD)
  • EnC Lambda and Closure Map (A643004C-0240-496F-A783-30D64F4979DE)
  • EnC State Machine State Map (8B78CD68-2EDE-420B-980B-E15884B8AAA3)

with serializers, deserializers, a portable-PDB read-back helper, and an occurrence-key packing helper for deterministic syntax-offset slots. It also plumbs an optional methodCustomDebugInfoRows side channel through the IL binary writer options into the portable PDB generator, so a compilation can attach CDI rows to named methods.

Why

F# hot reload (#19941) needs Roslyn-compatible EnC method debug information in baseline PDBs so deltas can be chained across generations the same way C# Edit and Continue does. This PR carries the format layer only, so it can be reviewed as pure metadata/PDB code with deterministic byte evidence, independent of the hot reload feature.

There is intentionally no in-tree caller populating the side channel yet; the consumer lands with #19941. This follows the same upstreaming pattern as #20017: land isolated, test-covered infrastructure first, wire the feature in a later PR.

Behavior

  • All existing writer call sites pass an empty map, so emitted PDBs are byte-identical to before (covered by a test asserting zero CustomDebugInformation rows for an empty map).
  • Method names that do not identify exactly one IL method row are dropped (nothing attached); covered by absent-name and ambiguous-name tests.
  • All additions are internal; no public surface area change.

Tests

17 tests in CompilerService/EncMethodDebugInformationTests.fs:

  • blob round-trips for all three formats, including negative baselines, temps, ordinal-flagged slots, and negative state numbers;
  • golden-byte encodings matching Roslyn's writer;
  • cross-validation decoding and re-encoding, byte for byte, CDI blobs emitted by a real Roslyn (C#) compilation;
  • fail-closed occurrence-key packing, including an int32-overflow regression where a wrapped negative key previously escaped the bound check;
  • end-to-end synthetic PDB emission through the IL writer proving correct MethodDef parenting, zero rows for the empty map, and no rows for absent or ambiguous names.

…ethod CDI emission

Adds an internal AbstractIL module implementing, byte for byte, the three Portable PDB
CustomDebugInformation blob formats Roslyn persists per method for Edit and Continue
(EnC Local Slot Map, EnC Lambda and Closure Map, EnC State Machine State Map), with
serializers, deserializers, a portable PDB read-back helper, and an occurrence-key
packing helper for deterministic syntax-offset slots.

Plumbs an optional methodCustomDebugInfoRows side channel through the IL binary writer
options into the portable PDB generator so a compilation can attach CDI rows to named
methods. Names that do not identify exactly one method row are dropped. All existing
writer call sites pass an empty map, so emitted PDBs are byte-identical to before.

No in-tree caller populates the map yet; the consumer is the F# hot reload work in
dotnet#19941, following the same pattern as dotnet#20017 (land isolated, test-covered
infrastructure first, wire the feature later).

Tests: blob round-trips, Roslyn golden-byte encodings, cross-validation against
CDI blobs emitted by a real Roslyn compilation, fail-closed occurrence-key packing
(including an int32-overflow regression where a wrapped negative key previously
escaped the bound check), and end-to-end synthetic PDB emission proving correct
MethodDef parenting, zero rows for an empty map, and no rows for absent or
ambiguous names.
@NatElkins NatElkins force-pushed the pdb-method-cdi-emission branch from b293239 to b7e5325 Compare July 1, 2026 21:08
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

❗ Release notes required

You can open this PR in browser to add release notes: open in github.dev


✅ Found changes and release notes in following paths:

Change path Release notes path Description
src/Compiler docs/release-notes/.FSharp.Compiler.Service/11.0.100.md

NatElkins added a commit to NatElkins/fsharp that referenced this pull request Jul 1, 2026
Adds an internal, standalone ECMA-335 Edit-and-Continue metadata delta writer to
AbstractIL: delta #- table stream and heap construction (DeltaMetadataTables,
DeltaMetadataSerializer, DeltaTableLayout, DeltaIndexSizing), ECMA-335 II.24.2.6
coded-index encoding (DeltaMetadataEncoding), EncLog/EncMap emission, generation GUID
chaining, user-string and standalone-signature token calculators (IlxDeltaStreams),
and the coordinating writer (FSharpDeltaMetadataWriter) over a plain row-description
input model (DeltaMetadataTypes, ILDeltaHandles, ILMetadataHeaps).

The writer's inputs are row records (names, tokens, signatures, RVAs) plus heap
offsets; it has no dependency on any semantic diffing or session machinery. It
compiles with no in-tree consumer by design: the consumer is the F# hot reload work
in dotnet#19941, following the same upstreaming pattern as dotnet#20017 and dotnet#20018
(land isolated, test-covered infrastructure first, wire the feature in a later PR).

One line of ilwrite.fsi is touched to expose the pre-existing markerForUnicodeBytes
so the delta writer reuses the exact string-marker logic of the full writer. No
behavior change for any existing code path.

Tests (130): coded-index encodings asserted against the production definitions and
ECMA-335 II.24.2.6 order, System.Reflection.Metadata reader parity over emitted
deltas, EncLog/EncMap correctness, stream layout, heap and index sizing,
multi-generation heap-offset chaining asserted against computed expected values,
standalone-signature rows asserted at baseline+1 from a real seeded baseline, and
serializer failure paths.
@NatElkins

Copy link
Copy Markdown
Contributor Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Commenter does not have sufficient privileges for PR 20018 in repo dotnet/fsharp

ProcessStartInfo.ArgumentList does not exist on net472, which the component tests
also target on Windows CI. Build the quoted argument string by hand instead.
@NatElkins NatElkins force-pushed the pdb-method-cdi-emission branch from 3489fce to 2844f2a Compare July 2, 2026 00:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: New

Development

Successfully merging this pull request may close these issues.

1 participant